Hallitse Django Admin -käyttöliittymää mukautetuilla toiminnoilla. Opi toteuttamaan tehokkaita massatoimintoja, datan vientiä ja integraatioita globaaleihin sovelluksiisi.
Django Adminin Voiman Vapauttaminen: Mukautetut Admin-toiminnot Selitettynä
Django Admin -käyttöliittymä on todella merkittävä työkalu, jota usein pidetään yhtenä viitekehyksen vaikuttavimmista ominaisuuksista. Valmiina se tarjoaa vankan, käyttäjäystävällisen ja turvallisen tavan hallita sovelluksesi dataa ilman, että hallintapaneeleihin tarvitsee kirjoittaa yhtäkään riviä backend-koodia. Monille projekteille tämä on enemmän kuin riittävästi. Sovellusten kasvaessa monimutkaisuutensa ja laajuutensa suhteen syntyy kuitenkin tarve erikoistuneemmille, tehokkaammille ja kontekstisidonnaisemmille toiminnoille, jotka menevät yksinkertaisia CRUD (Create, Read, Update, Delete) -tehtäviä pidemmälle.
Tässä kohtaa Djangon Mukautetut Admin-toiminnot astuvat kuvaan. Admin-toiminnot antavat kehittäjille mahdollisuuden määritellä tiettyjä operaatioita, jotka voidaan suorittaa valituille objektijoukoille suoraan muutosten listaus -sivulta. Kuvittele, että voit merkitä satoja käyttäjätilejä "epäaktiivisiksi", luoda räätälöidyn raportin valituista tilauksista tai synkronoida tuotepäivitysten erän ulkoisen verkkokauppa-alustan kanssa – kaikki muutamalla napsautuksella tutussa Django Adminissa. Tämä opas vie sinut kattavalle matkalle ymmärtämään, toteuttamaan ja hallitsemaan mukautettuja admin-toimintoja, antaen sinulle mahdollisuuden laajentaa hallinnollisia kyvykkyyksiäsi merkittävästi mille tahansa globaalille sovellukselle.
Djangon Adminin Ydinvoiman Ymmärtäminen
Ennen mukauttamiseen syventymistä on tärkeää arvostaa Djangon Adminin perustavanlaatuista voimaa. Se ei ole vain perus backend; se on dynaaminen, mallipohjainen käyttöliittymä, joka:
- Generoi Lomakkeet Automaattisesti: Malliesi perusteella se luo lomakkeita datan lisäämistä ja muokkausta varten.
- Hallitsee Suhteita: Hallitsee vierasavaimia, monta-moneen ja yhden-yhteen -suhteita intuitiivisilla widgeteillä.
- Tarjoaa Tunnistautumisen & Valtuutuksen: Integroituu saumattomasti Djangon vankkaan käyttäjä- ja oikeusjärjestelmään.
- Tarjoaa Suodatuksen & Haun: Antaa ylläpitäjien nopeasti löytää tiettyjä datarivejä.
- Tukee Kansainvälistämistä: Valmis globaaliin käyttöönottoon sisäänrakennetuilla käyttöliittymän käännösominaisuuksilla.
Tämä valmis toiminnallisuus vähentää huomattavasti kehitysaikaa ja varmistaa yhdenmukaisen, turvallisen hallintaportaalin datallesi. Mukautetut admin-toiminnot rakentuvat tämän vahvan perustan päälle tarjoten koukun yrityspohjaisen logiikan mukaisille operaatioille.
Miksi Mukautetut Admin-toiminnot Ovat Välttämättömiä
Vaikka oletusarvoinen admin-käyttöliittymä on erinomainen yksittäisten objektien hallintaan, se usein riittämätön operaatioille, jotka koskevat useita objekteja tai vaativat monimutkaista yrityspohjaista logiikkaa. Tässä on joitain vakuuttavia skenaarioita, joissa mukautetut admin-toiminnot tulevat välttämättömiksi:
-
Massadatan Toiminnot: Kuvittele hallitsevasi verkkokurssialustaa tuhansilla kursseilla. Saatat joutua:
- Merkitä useita kursseja "julkaistuiksi" tai "luonnoksiksi".
- Määrittää uuden ohjaajan valitulle kurssiryhmälle.
- Poistamaan vanhentuneiden opiskelijoiden ilmoittautumisten erän.
-
Datan Synkronointi & Integraatio: Sovellukset ovat usein vuorovaikutuksessa ulkoisten järjestelmien kanssa. Admin-toiminnot voivat helpottaa:
- Valittujen tuotepäivitysten lähettämistä ulkoiseen API:in (esim. varastonhallintajärjestelmä, maksuyhdyskäytävä tai globaali verkkokauppa-alusta).
- Datan uudelleenhakuprosessin käynnistämistä valitulle sisällölle hakukoneessa.
- Tilausten merkitsemistä "lähetetyiksi" ulkoisessa logistiikkajärjestelmässä.
-
Mukautetut Raportit & Vienti: Vaikka Django admin tarjoaa perusviennin, saatat tarvita erittäin spesifejä raportteja:
- Luomaan CSV-tiedoston valituista käyttäjäpostiosoitteista markkinointikampanjaa varten.
- Luomaan PDF-yhteenvedon laskuista tietylle ajanjaksolle.
- Viemään taloustietoja integraatiota varten kirjanpitojärjestelmän kanssa.
-
Työnkulkujen Hallinta: Monimutkaisilla työnkuluilla varustetuissa sovelluksissa toiminnot voivat virtaviivaistaa prosesseja:
- Hyväksyä tai hylätä useita odottavia käyttäjärekisteröintejä.
- Siirtää valitut tukipyynnöt "ratkaistu"-tilaan.
- Käynnistää sähköposti-ilmoituksen käyttäjäryhmälle.
-
Automaattisten Tehtävien Käynnistimet: Joskus admin-toiminto voi yksinkertaisesti käynnistää pidemmän prosessin:
- Käynnistetään päivittäinen datavarmuuskopiointi tietylle datasetille.
- Ajetaan datan migraatioskripti valituilla riveillä.
Nämä skenaariot korostavat, kuinka mukautetut admin-toiminnot yhdistävät yksinkertaiset hallinnolliset tehtävät monimutkaisiin, yrityskriittisiin operaatioihin, tehden Django Administa todella kattavan hallintaportaalin.
Perus Mukautetun Admin-toiminnon Anatomia
Ytimeltään Django admin-toiminto on Python-funktio tai metodi ModelAdmin
-luokassasi. Se ottaa kolme argumenttia: modeladmin
, request
ja queryset
.
modeladmin
: Tämä on nykyinenModelAdmin
-instanssi. Se tarjoaa pääsyn erilaisiin apumetodeihin ja attribuutteihin, jotka liittyvät hallinnoitavaan malliin.request
: Nykyinen HTTP-pyyntöobjekti. Tämä on standardi DjangoHttpRequest
-objekti, joka antaa sinulle pääsyn käyttäjätietoihin, POST/GET-dataan, istuntodataan jne.queryset
: Tällä hetkellä valittujen objektienQuerySet
. Tämä on ratkaiseva osa, koska se sisältää kaikki malli-instanssit, joihin toiminnon tulee vaikuttaa.
Toimintofunktion tulisi ideaalisesti palauttaa HttpResponseRedirect
alkuperäiseen muutosten listaus -sivuun sujuvan käyttäjäkokemuksen varmistamiseksi. Jos se ei palauta mitään (tai palauttaa None
), admin lataa yksinkertaisesti nykyisen sivun uudelleen. On myös hyvä käytäntö tarjota käyttäjäpalautetta Djangon viestijärjestelmän avulla.
Vaiheittain: Ensimmäisen Mukautetun Admin-toiminnon Toteuttaminen
Luodaan käytännön esimerkki. Oletetaan, että meillä on Product
-malli, ja haluamme toiminnon, joka merkitsee valitut tuotteet "alennetuiksi".
# myapp/models.py
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=10, decimal_places=2)
is_discounted = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
Lisätään nyt mukautettu admin-toiminto tiedostoon myapp/admin.py
:
# myapp/admin.py
from django.contrib import admin, messages
from django.db.models import QuerySet
from django.http import HttpRequest
from .models import Product
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ('name', 'price', 'is_discounted', 'created_at')
list_filter = ('is_discounted', 'created_at')
search_fields = ('name',)
# Määritellään mukautettu admin-toimintofunktio
def make_discounted(self, request: HttpRequest, queryset: QuerySet):
updated_count = queryset.update(is_discounted=True)
self.message_user(
request,
f"{updated_count} tuotetta merkittiin onnistuneesti alennetuiksi.",
messages.SUCCESS
)
make_discounted.short_description = "Merkitse valitut tuotteet alennetuiksi"
# Rekisteröidään toiminto ModelAdminiin
actions = [make_discounted]
Selitys:
- Toimintofunktio: Määritimme
make_discounted
:n metodiksiProductAdmin
-luokassa. Tämä on suositeltu lähestymistapa toiminnoille, jotka ovat spesifejä yhdelleModelAdmin
-instanssille. - Signatuuri: Se hyväksyy oikein
self
(koska se on metodi),request
jaqueryset
. - Logiikka: Funktion sisällä käytämme
queryset.update(is_discounted=True)
-komentoa päivittääksemme tehokkaasti kaikki valitut objektit yhdessä tietokantakyselyssä. Tämä on paljon suorituskykyisempää kuin kyselyn läpikäyminen ja jokaisen objektin tallentaminen erikseen. - Käyttäjäpalautus:
self.message_user()
on kätevä metodi, jonkaModelAdmin
tarjoaa viestien näyttämiseksi käyttäjälle admin-käyttöliittymässä. Käytämmemessages.SUCCESS
positiiviseen ilmoitukseen. short_description
: Tämä attribuutti määrittelee käyttäjäystävällisen nimen, joka ilmestyy "Toiminto"-pudotusvalikkoon adminissa. Ilman sitä funktion raaka nimi (esim. "make_discounted") näkyisi, mikä ei ole ihanteellista käyttäjälle.actions
-lista: Lopuksi rekisteröimme toimintomme lisäämällä sen funktion viittauksenactions
-listaanProductAdmin
-luokassamme.
Nyt jos navigoit Django Adminin Tuotteiden muutosten listaus -sivulle, valitset muutaman tuotteen ja valitset pudotusvalikosta "Merkitse valitut tuotteet alennetuiksi", valitut kohteet päivittyvät ja näet onnistumisviestin.
Toimintojen Parantaminen Käyttäjän Vahvistuksella: Epäonnistuneiden Toimintojen Estäminen
Suorittamalla suoraan toiminnon kuten "poista kaikki valitut" tai "julkaisse kaikki sisällöt" ilman vahvistusta voi johtaa merkittävään datan menetykseen tai tahattomiin seurauksiin. Tärkeille toiminnoille on välttämätöntä lisätä välikappaleen vahvistusvaihe. Tämä vaatii tyypillisesti mukautetun mallin renderöinnin vahvistuslomakkeella.
Tarkennetaan make_discounted
-toimintoamme sisällyttämään vahvistusvaihe. Teemme siitä hieman yleisemmän havainnollistamistarkoituksessa, ehkä "Merkitse kohteet "Hyväksytyiksi" vahvistuksella".
# myapp/models.py (oletetaan Post-malli)
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
status = models.CharField(max_length=20, default='draft', choices=[
('draft', 'Luonnos'),
('pending', 'Tarkistettavana'),
('approved', 'Hyväksytty'),
('rejected', 'Hylätty'),
])
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
Ensin tarvitsemme yksinkertaisen lomakkeen vahvistusta varten:
# myapp/forms.py
from django import forms
class ConfirmationForm(forms.Form):
confirm = forms.BooleanField(
label="Oletko varma, että haluat suorittaa tämän toiminnon?",
required=True,
widget=forms.HiddenInput # Hoidamme näytön mallissa
)
_selected_action = forms.CharField(widget=forms.HiddenInput)
action = forms.CharField(widget=forms.HiddenInput)
Seuraavaksi toiminto tiedostossa myapp/admin.py
:
# myapp/admin.py
from django.contrib import admin, messages
from django.db.models import QuerySet
from django.http import HttpRequest, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from .models import Post
from .forms import ConfirmationForm
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'status', 'created_at')
list_filter = ('status',)
search_fields = ('title',)
def mark_posts_approved(self, request: HttpRequest, queryset: QuerySet) -> HttpResponseRedirect | None:
# Tarkistetaan, onko käyttäjä vahvistanut toiminnon
if 'apply' in request.POST:
form = ConfirmationForm(request.POST)
if form.is_valid():
updated_count = queryset.update(status='approved')
self.message_user(
request,
f"{updated_count} julkaisua merkittiin onnistuneesti hyväksytyiksi.",
messages.SUCCESS
)
return HttpResponseRedirect(request.get_full_path())
# Jos ei vahvistettu, tai GET-pyyntö, näytetään vahvistussivu
else:
# Tallennetaan valittujen objektien ensisijaiset avaimet piilotettuun kenttään
# Tämä on välttämätöntä valinnan välittämiseksi vahvistussivun yli
context = self.admin_site.each_context(request)
context['queryset'] = queryset
context['form'] = ConfirmationForm(initial={
'_selected_action': request.POST.getlist(admin.ACTION_CHECKBOX_NAME),
'action': 'mark_posts_approved',
})
context['action_name'] = self.mark_posts_approved.short_description
context['title'] = _("Vahvista toiminto")
# Renderöidään mukautettu vahvistusmalli
return render(request, 'admin/confirmation_action.html', context)
mark_posts_approved.short_description = _("Merkitse valitut julkaisut hyväksytyiksi")
actions = [mark_posts_approved]
Ja vastaava malli (templates/admin/confirmation_action.html
):
{# templates/admin/confirmation_action.html #}
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static admin_modify %}
{% block extrastyle %}{{ block.super }}
{% endblock %}
{% block content %}
{% endblock %}
Jotta malli olisi löydettävissä, varmista, että sinulla on templates
-hakemisto sovelluksesi sisällä (myapp/templates/admin/
) tai määritetty settings.py
tiedoston TEMPLATES
-asetuksissa.
Keskeiset elementit vahvistustoiminnoille:
- Ehdollinen Logiikka: Toiminto tarkistaa
if 'apply' in request.POST:
. Jos käyttäjä on lähettänyt vahvistuslomakkeen, toiminto etenee. Muuten se renderöi vahvistussivun. _selected_action
: Tämä piilotettu kenttä on ratkaiseva. Django admin lähettää valittujen objektien ensisijaiset avaimet POST-parametrin kautta nimeltäaction_checkbox
. Vahvistuslomaketta renderöitäessä poimimme nämä ID:t käyttämällärequest.POST.getlist(admin.ACTION_CHECKBOX_NAME)
ja välitämme ne takaisin piilotettuina syötteinä vahvistuslomakkeessamme. Tämä varmistaa, että kun käyttäjä vahvistaa, alkuperäinen valinta lähetetään uudelleen toiminnolle.- Mukautettu Lomake: Yksinkertainen
forms.Form
käytetään käyttäjän vahvistuksen tallentamiseen. Vaikka käytämme piilotettua syötettäconfirm
-kentälle, malli näyttää kysymyksen suoraan. - Mallin Renderöinti: Käytämme
django.shortcuts.render()
-komentoa näyttääksemme mukautetunconfirmation_action.html
-mallimme. Välitämmequeryset
jaform
malliin näytettäväksi. - CSRF-suojaus: Sisällytä aina
{% csrf_token %}
lomakkeisiin suojautuaksesi Cross-Site Request Forgery -hyökkäyksiltä. - Palautusarvo: Onnistuneen suorituksen jälkeen palautamme
HttpResponseRedirect(request.get_full_path())
lähettääksemme käyttäjän takaisin adminin muutosten listaus -sivulle, estäen kaksoislomakkeen lähetyksen, jos he päivittävät sivun.
Tämä malli tarjoaa vankan tavan toteuttaa vahvistusdialogeja kriittisille admin-toiminnoille, parantaen käyttäjäkokemusta ja estäen kalliita virheitä.
Käyttäjän Syötteen Lisääminen Toimintoihin: Dynaamiset Operaatiot
Joskus yksinkertainen "kyllä/ei"-vahvistus ei riitä. Saatat tarvita ylläpitäjän syöttävän lisätietoja, kuten syyn toiminnalle, uuden arvon kentälle tai valinnan ennalta määritellystä listasta. Tämä vaatii monimutkaisempien lomakkeiden sisällyttämistä admin-toimintoihisi.
Tarkastellaan esimerkkiä: toiminto "Muuta tila ja lisää kommentti" valituille Post
-objekteille.
# myapp/forms.py
from django import forms
from .models import Post
class ChangePostStatusForm(forms.Form):
_selected_action = forms.CharField(widget=forms.HiddenInput)
action = forms.CharField(widget=forms.HiddenInput)
new_status = forms.ChoiceField(
label="Uusi tila",
choices=Post.STATUS_CHOICES, # Olettaen, että STATUS_CHOICES on määritelty Post-mallissa
required=True
)
comment = forms.CharField(
label="Syy/Kommentti (valinnainen)",
required=False,
widget=forms.Textarea(attrs={'rows': 3})
)
# Lisää STATUS_CHOICES Post-malliin
# myapp/models.py
from django.db import models
class Post(models.Model):
STATUS_CHOICES = [
('draft', 'Luonnos'),
('pending', 'Tarkistettavana'),
('approved', 'Hyväksytty'),
('rejected', 'Hylätty'),
]
title = models.CharField(max_length=255)
content = models.TextField()
status = models.CharField(max_length=20, default='draft', choices=STATUS_CHOICES)
comment_history = models.TextField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
Nyt toiminto tiedostossa myapp/admin.py
:
# myapp/admin.py (jatkoa)
from django.contrib import admin, messages
from django.db.models import QuerySet
from django.http import HttpRequest, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from .models import Post
from .forms import ChangePostStatusForm # Tuodaan uusi lomake
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'status', 'created_at')
list_filter = ('status',)
search_fields = ('title',)
# Olemassa oleva mark_posts_approved toiminto...
def change_post_status_with_comment(self, request: HttpRequest, queryset: QuerySet) -> HttpResponseRedirect | None:
form = None
if 'apply' in request.POST:
form = ChangePostStatusForm(request.POST)
if form.is_valid():
new_status = form.cleaned_data['new_status']
comment = form.cleaned_data['comment']
updated_count = 0
for post in queryset:
post.status = new_status
if comment:
post.comment_history = (post.comment_history or '') + f"\n[{request.user.username}] muutettu tilaan {new_status} kommentilla: {comment}"
post.save()
updated_count += 1
self.message_user(
request,
f"{updated_count} julkaisun tila muutettiin tilaan '{new_status}' ja kommentti lisättiin.",
messages.SUCCESS
)
return HttpResponseRedirect(request.get_full_path())
# Jos ei vahvistettu, tai GET-pyyntö, näytetään syöttölomake
if not form:
form = ChangePostStatusForm(initial={
'_selected_action': request.POST.getlist(admin.ACTION_CHECKBOX_NAME),
'action': 'change_post_status_with_comment',
})
context = self.admin_site.each_context(request)
context['queryset'] = queryset
context['form'] = form
context['action_name'] = self.change_post_status_with_comment.short_description
context['title'] = _("Muuta julkaisun tilaa ja lisää kommentti")
return render(request, 'admin/change_status_action.html', context)
change_post_status_with_comment.short_description = _("Muuta valittujen julkaisujen tilaa (kommentilla)")
actions = [
mark_posts_approved,
change_post_status_with_comment
]
Ja vastaava malli (templates/admin/change_status_action.html
):
{# templates/admin/change_status_action.html #}
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static admin_modify %}
{% block extrastyle %}{{ block.super }}
{% endblock %}
{% block content %}
{% endblock %}
Tärkeimmät Huomiot Käyttäjän Syötettä Sisältäviin Toimintoihin:
- Erillinen Lomake: Luo erillinen
forms.Form
(taiforms.ModelForm
, jos vuorovaikutetaan yhden malli-instanssin kanssa) tallentaaksesi kaikki tarvittavat käyttäjän syötteet. - Lomakkeen Validaatio: Djangon lomakkeen validointi hoitaa datan eheyden ja virheilmoitukset automaattisesti. Tarkista
if form.is_valid():
ennenform.cleaned_data
:n käyttöä. - Iterointi vs. Massapäivitys: Huomaa, että
comment_history
-kenttään tekstin lisäämiseksi käymme läpi kyselyn ja tallennamme jokaisen objektin erikseen. Tämä johtuu siitä, että.update()
ei voi suorittaa monimutkaista logiikkaa, kuten tekstin lisäämistä olemassa olevaan kenttään jokaiselle objektille. Vaikka se on vähemmän suorituskykyistä erittäin suurille kyselyjoukoille, se on välttämätöntä objekti-kohtaista logiikkaa vaativissa operaatioissa. Yksinkertaisille kenttäpäivityksillequeryset.update()
on suositeltavaa. - Lomakkeen Uudelleenrenderöinti Virheillä: Jos
form.is_valid()
palauttaaFalse
,render()
-funktio näyttää lomakkeen uudelleen, sisältäen automaattisesti validoinnin virheet, mikä on standardi Django-lomakkeiden käsittelymalli.
Tämä lähestymistapa mahdollistaa erittäin joustavat ja dynaamiset hallinnolliset operaatiot, joissa ylläpitäjä voi antaa toiminnolle spesifejä parametreja.
Edistyneet Mukautetut Admin-toiminnot: Perusteita Pitemmälle
Todellinen mukautettujen admin-toimintojen voima ilmenee integroitumisessa ulkoisiin palveluihin, monimutkaisten raporttien luomisessa tai pitkään kestävien tehtävien suorittamisessa. Tutustutaan joihinkin edistyneisiin käyttötapauksiin.
1. Ulkoisten API-kutsujen Käyttö Datan Synkronointiin
Kuvittele, että Django-sovelluksesi hallitsee tuotekatalogia, ja sinun on synkronoitava valitut tuotteet ulkoisen verkkokauppa-alustan tai globaalin varastonhallintajärjestelmän (IMS) kanssa sen API:n kautta. Admin-toiminto voi käynnistää tämän synkronoinnin.
Oletetaan, että meillä on Product
-malli kuten aiemmin määritelty, ja haluamme lähettää päivityksiä valituista tuotteista ulkoiseen varastonhallintapalveluun.
# myapp/admin.py (jatkoa)
import requests # Sinun tulee asentaa requests: pip install requests
# ... muut tuonnit ...
# Olettaen aiemman ProductAdminin
class ProductAdmin(admin.ModelAdmin):
# ... olemassa olevat list_display, list_filter, search_fields ...
def sync_products_to_external_ims(self, request: HttpRequest, queryset: QuerySet) -> HttpResponseRedirect | None:
# Tarkista vahvistus (samankaltainen kuin edellisissä esimerkeissä, jos tarpeen)
if 'apply' in request.POST:
# Simuloidaan ulkoista API-päätepistettä
EXTERNAL_IMS_API_URL = "https://api.example.com/v1/products/sync/"
API_KEY = "your_secret_api_key" # Todellisessa sovelluksessa käytä settings.py:tä tai ympäristömuuttujia
successful_syncs = 0
failed_syncs = []
for product in queryset:
data = {
"product_id": product.id,
"name": product.name,
"price": str(product.price), # Muunnetaan Decimal merkkijonoksi JSON:ia varten
"is_discounted": product.is_discounted,
# Lisää muu relevantti tuotetieto
}
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
try:
response = requests.post(EXTERNAL_IMS_API_URL, json=data, headers=headers, timeout=5) # 5 sekunnin aikakatkaisu
response.raise_for_status() # Nostaa HTTPErrorin virheellisille vastauksille (4xx tai 5xx)
successful_syncs += 1
except requests.exceptions.RequestException as e:
failed_syncs.append(f"Tuote {product.name} (ID: {product.id}): {e}")
except Exception as e:
failed_syncs.append(f"Tuote {product.name} (ID: {product.id}): Odottamaton virhe: {e}")
if successful_syncs > 0:
self.message_user(
request,
f"{successful_syncs} tuotetta synkronoitiin onnistuneesti ulkoiseen IMS:iin.",
messages.SUCCESS
)
if failed_syncs:
error_message = f"Epäonnistui {len(failed_syncs)} tuotteen synkronointi:\n" + "\n".join(failed_syncs)
self.message_user(request, error_message, messages.ERROR)
return HttpResponseRedirect(request.get_full_path())
# Alkuperäinen GET-pyyntö tai ei-apply POST-pyyntö: näytä vahvistus (jos halutaan)
context = self.admin_site.each_context(request)
context['queryset'] = queryset
context['form'] = ConfirmationForm(initial={
'_selected_action': request.POST.getlist(admin.ACTION_CHECKBOX_NAME),
'action': 'sync_products_to_external_ims',
})
context['action_name'] = self.sync_products_to_external_ims.short_description
context['title'] = _("Vahvista datan synkronointi")
return render(request, 'admin/confirmation_action.html', context) # Käytetään uudelleen vahvistusmallia
sync_products_to_external_ims.short_description = _("Synkronoi valitut tuotteet ulkoiseen IMS:iin")
actions = [
# ... muut toiminnot ...
sync_products_to_external_ims,
]
Tärkeitä Huomioita API-integraatioihin:
- Virheenkäsittely: Vankat
try-except
-lohkot ovat ratkaisevia verkkopyynnöille. Käsittele yhteysvirheet, aikakatkaisut ja API-kohtaiset virheet (esim. 401 Unauthorized, 404 Not Found, 500 Internal Server Error). - Turvallisuus: API-avaimia ja arkaluonteisia tunnuksia ei koskaan pidä kovakoodata. Käytä Djangon asetuksia (esim.
settings.EXTERNAL_API_KEY
) tai ympäristömuuttujia. - Suorituskyky: Jos synkronoidaan monia kohteita, harkitse API-pyyntöjen eräistämistä tai, mikä vielä parempaa, asynkronisten tehtävien käyttöä (katso alla).
- Käyttäjäpalautus: Anna selkeitä viestejä siitä, mitkä kohteet onnistuivat ja mitkä epäonnistuivat, virhetietojen kanssa.
2. Raporttien ja Datan Viennin (CSV/Excel) Luominen
Valitun datan vienti on hyvin yleinen vaatimus. Django admin-toimintoja voidaan käyttää räätälöityjen CSV- tai jopa Excel-tiedostojen luomiseen suoraan valitusta kyselystä.
Luodaan toiminto valittujen Post
-datan viemiseksi CSV-tiedostoon.
# myapp/admin.py (jatkoa)
import csv
from django.http import HttpResponse
# ... muut tuonnit ...
class PostAdmin(admin.ModelAdmin):
# ... olemassa olevat list_display, list_filter, search_fields, actions ...
def export_posts_as_csv(self, request: HttpRequest, queryset: QuerySet) -> HttpResponse:
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="posts_export.csv"'
writer = csv.writer(response)
# Kirjoitetaan otsikkorivi
writer.writerow(['Otsikko', 'Tila', 'Luotu', 'Sisällön esikatselu'])
for post in queryset:
writer.writerow([
post.title,
post.get_status_display(), # Käytä get_FOO_display()-metodia valintakentille
post.created_at.strftime("%Y-%m-%d %H:%M:%S"),
post.content[:100] + '...' if len(post.content) > 100 else post.content # Katkaistaan pitkät sisällöt
])
self.message_user(
request,
f"{queryset.count()} julkaisua vietiin onnistuneesti CSV:iin.",
messages.SUCCESS
)
return response
export_posts_as_csv.short_description = _("Vie valitut julkaisut CSV:nä")
actions = [
# ... muut toiminnot ...
export_posts_as_csv,
]
CSV- ja Excel-vientiin: Käyttäisit tyypillisesti kirjastoa kuten openpyxl
tai pandas
. Periaate on samanlainen: luodaan tiedosto muistiin ja liitetään se HttpResponse
:iin oikealla Content-Type
-otsakkeella (esim. application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
.xlsx-tiedostoille).
3. Asynkroniset Toiminnot Pitkään Kestäville Tehtäville
Jos admin-toiminto sisältää merkittävästi aikaa vieviä operaatioita (esim. suurten datasettien käsittely, monimutkaisten raporttien luominen, hitaiden ulkoisten API:iden kanssa vuorovaikutus), niiden suorittaminen synkronisesti estää web-palvelimen ja johtaa aikakatkaisuihin tai huonoon käyttäjäkokemukseen. Ratkaisu on siirtää nämä tehtävät taustatyöntekijälle käyttämällä tehtäväjono-järjestelmää, kuten Celeryä.
Edellytykset:
- Celery: Asenna Celery ja välittäjä (esim. Redis tai RabbitMQ).
- Django-Celery-Results: Valinnainen, mutta hyödyllinen tehtävien tulosten tallentamiseen tietokantaan.
Muokataan API-synkronointiesimerkkiä asynkroniseksi.
# myproject/celery.py (standardi Celery-asetus)
import os
from celery import Celery
# Asetetaan oletus Django-asetusmoduuli 'celery'-ohjelmalle.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
app = Celery('myproject')
# Käyttämällä merkkijonoa tässä työntekijä ei joudu
# picklaamaan oliota, kun käytetään Windowsia.
app.config_from_object('django.conf:settings', namespace='CELERY')
# Ladataan tehtävämoduulit kaikista rekisteröidyistä Django-sovelluskonfiguraatioista.
app.autodiscover_tasks()
@app.task(bind=True)
def debug_task(self):
print(f'Request: {self.request!r}')
# myapp/tasks.py
import requests
from celery import shared_task
from django.contrib.auth import get_user_model
from django.apps import apps
@shared_task
def sync_product_to_external_ims_task(product_id, admin_user_id):
Product = apps.get_model('myapp', 'Product')
User = get_user_model()
try:
product = Product.objects.get(pk=product_id)
admin_user = User.objects.get(pk=admin_user_id)
EXTERNAL_IMS_API_URL = "https://api.example.com/v1/products/sync/"
API_KEY = "your_secret_api_key" # Käytä ympäristömuuttujia tai Django-asetuksia
data = {
"product_id": product.id,
"name": product.name,
"price": str(product.price),
"is_discounted": product.is_discounted,
}
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
response = requests.post(EXTERNAL_IMS_API_URL, json=data, headers=headers, timeout=10)
response.raise_for_status()
# Kirjaa onnistuminen (esim. Django-lokkeihin tai tiettyyn malliin seurantaa varten)
print(f"Tuote {product.name} (ID: {product.id}) synkronoitu onnistuneesti käyttäjän {admin_user.username} toimesta.")
except Product.DoesNotExist:
print(f"Tuotetta ID:llä {product_id} ei löytynyt.")
except User.DoesNotExist:
print(f"Ylläpitäjää ID:llä {admin_user_id} ei löytynyt.")
except requests.exceptions.RequestException as e:
print(f"API-synkronointi epäonnistui tuotteelle {product_id}: {e}")
except Exception as e:
print(f"Odottamaton virhe synkronoinnin aikana tuotteelle {product_id}: {e}")
# myapp/admin.py (jatkoa)
from django.contrib import admin, messages
from django.db.models import QuerySet
from django.http import HttpRequest, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from .models import Product # Oletetaan aiempi Product-malli
from .tasks import sync_product_to_external_ims_task # Tuodaan Celery-tehtäväsi
class ProductAdmin(admin.ModelAdmin):
# ... olemassa olevat list_display, list_filter, search_fields ...
def async_sync_products_to_external_ims(self, request: HttpRequest, queryset: QuerySet) -> HttpResponseRedirect | None:
if 'apply' in request.POST:
admin_user_id = request.user.id
for product in queryset:
# Jonotetaan tehtävä jokaiselle valitulle tuotteelle
sync_product_to_external_ims_task.delay(product.id, admin_user_id)
self.message_user(
request,
f"{queryset.count()} tuotteen synkronointitehtävät on jonotettu.",
messages.SUCCESS
)
return HttpResponseRedirect(request.get_full_path())
# Alkuperäinen GET-pyyntö tai ei-apply POST-pyyntö: näytä vahvistus
context = self.admin_site.each_context(request)
context['queryset'] = queryset
context['form'] = ConfirmationForm(initial={
'_selected_action': request.POST.getlist(admin.ACTION_CHECKBOX_NAME),
'action': 'async_sync_products_to_external_ims',
})
context['action_name'] = self.async_sync_products_to_external_ims.short_description
context['title'] = _("Vahvista asynkroninen datan synkronointi")
return render(request, 'admin/confirmation_action.html', context) # Käytetään uudelleen vahvistusmallia
async_sync_products_to_external_ims.short_description = _("Jonoa asynkroninen synkronointi valituille tuotteille IMS:iin")
actions = [
# ... muut toiminnot ...
async_sync_products_to_external_ims,
]
Kuinka tämä toimii:
- Admin-toiminto, sen sijaan että suorittaisi raskaan työn suoraan, käy läpi valitun kyselyn.
- Jokaiselle valitulle objektille se kutsuu
.delay()
-metodia Celery-tehtävässä, välittäen tarvittavat parametrit (esim. ensisijainen avain, käyttäjän ID). Tämä jonottaa tehtävän. - Admin-toiminto palauttaa välittömästi
HttpResponseRedirect
-objektin ja onnistumisviestin, ilmoittaen käyttäjälle, että tehtävät on jonotettu. Web-pyyntö on lyhytikäinen. - Taustalla Celery-työntekijät nappaavat nämä tehtävät välittäjältä ja suorittavat ne, erillään web-pyynnöstä.
Monimutkaisemmissa skenaarioissa saatat haluta seurata tehtävien edistymistä ja tuloksia adminissa. Kirjastot kuten django-celery-results
voivat tallentaa tehtävien tiloja tietokantaan, jolloin voit näyttää linkin tila-sivulle tai jopa päivittää admin-käyttöliittymää dynaamisesti.
Parhaat Käytännöt Mukautetuille Admin-toiminnoille
Varmistaaksesi, että mukautetut admin-toiminnot ovat vankkoja, turvallisia ja ylläpidettäviä, noudata näitä parhaita käytäntöjä:
1. Oikeudet ja Valtuutus
Kaikilla ylläpitäjillä ei tulisi olla pääsyä kaikkiin toimintoihin. Voit hallita, kuka näkee ja voi suorittaa toiminnon, käyttämällä Djangon oikeusjärjestelmää.
Tapa 1: Käyttämällä has_perm()
Voit tarkistaa spesifejä oikeuksia toimintofunktion sisällä:
def sensitive_action(self, request, queryset):
if not request.user.has_perm('myapp.can_perform_sensitive_action'):
self.message_user(request, _("Sinulla ei ole oikeutta suorittaa tätä toimintoa."), messages.ERROR)
return HttpResponseRedirect(request.get_full_path())
# ... arkaluonteisen toiminnon logiikka ...
Määritä sitten mukautettu oikeus myapp/models.py
-tiedostoon Meta
-luokassa:
# myapp/models.py
class Product(models.Model):
# ... kentät ...
class Meta:
permissions = [
("can_perform_sensitive_action", "Voi suorittaa arkaluonteisen tuotetoiminnon"),
]
Suoritettuasi `makemigrations` ja `migrate`, tämä oikeus ilmestyy Django Adminiin käyttäjille ja ryhmille.
Tapa 2: Toimintojen Dynaaminen Rajoittaminen get_actions()
-metodin Kautta
Voit korvata get_actions()
-metodin ModelAdmin
-luokassasi ehdollisesti poistaaksesi toimintoja nykyisen käyttäjän oikeuksien perusteella:
# myapp/admin.py
class ProductAdmin(admin.ModelAdmin):
# ... toimintojen määrittely ...
def get_actions(self, request: HttpRequest):
actions = super().get_actions(request)
# Poista 'make_discounted'-toiminto, jos käyttäjällä ei ole spesifiä oikeutta
if not request.user.has_perm('myapp.change_product'): # Tai mukautettu oikeus kuten 'can_discount_product'
if 'make_discounted' in actions:
del actions['make_discounted']
return actions
Tämä lähestymistapa tekee toiminnosta täysin näkymättömän valtuuttamattomille käyttäjille, tarjoten selkeämmän käyttöliittymän.
2. Vankka Virheenkäsittely
Ennakoi epäonnistumisia ja käsittele ne sulavasti. Käytä try-except
-lohkoja tietokantaoperaatioiden, ulkoisten API-kutsujen ja tiedosto-operaatioiden ympärillä. Tarjoa informatiivisia virheviestejä käyttäjälle käyttämällä self.message_user(request, ..., messages.ERROR)
.
3. Käyttäjäpalautteet ja Viestit
Ilmoita aina käyttäjälle toiminnon tuloksesta. Djangon viestijärjestelmä on ihanteellinen tähän:
messages.SUCCESS
: Onnistuneisiin operaatioihin.messages.WARNING
: Osittaisiin onnistumisiin tai pieniin ongelmiin.messages.ERROR
: Kriittisiin epäonnistumisiin.messages.INFO
: Yleisiin informatiivisiin viesteihin (esim. "Tehtävä jonotettu onnistuneesti.").
4. Suorituskykyhuomiot
- Massatoiminnot: Käytä aina mahdollisuuksien mukaan
queryset.update()
taiqueryset.delete()
massatietokantaoperaatioihin. Nämä suorittavat yhden SQL-kyselyn ja ovat huomattavasti tehokkaampia kuin jokaisen objektin läpikäynti ja tallentaminen/poistaminen erikseen. - Atomiset Transaktiot: Toiminnoille, jotka sisältävät useita tietokantamuutoksia, joiden on onnistuttava tai epäonnistuttava kokonaisuutena, käärise logiikkasi transaktioon käyttämällä
from django.db import transaction
jawith transaction.atomic():
. - Asynkroniset Tehtävät: Pitkään kestävien operaatioiden (API-kutsut, raskaat laskelmat, tiedostojen käsittely) osalta siirrä ne taustatehtäväjonoon (esim. Celery) estääksesi web-palvelimen tukkeutumisen.
5. Uudelleenkäytettävyys ja Järjestely
Jos sinulla on toimintoja, jotka voivat olla hyödyllisiä useiden ModelAdmin
-luokkien tai jopa eri projektien välillä, harkitse niiden kapselointia:
- Itsenäiset Funktiot: Määrittele toiminnot itsenäisinä funktioina
myapp/admin_actions.py
-tiedostossa ja tuo neModelAdmin
-luokkiisi. - Mixinit: Monimutkaisemmille toiminnoille, joihin liittyy lomakkeita tai malleja, luo
ModelAdmin
-luokkamixini.
# myapp/admin_actions.py
from django.contrib import messages
from django.http import HttpRequest, HttpResponseRedirect
from django.db.models import QuerySet
def mark_as_active(modeladmin, request: HttpRequest, queryset: QuerySet):
updated = queryset.update(is_active=True)
modeladmin.message_user(request, f"{updated} kohdetta merkittiin aktiivisiksi.", messages.SUCCESS)
mark_as_active.short_description = "Merkitse valitut aktiivisiksi"
# myapp/admin.py
from django.contrib import admin
from .models import MyModel
from .admin_actions import mark_as_active
@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
list_display = ('name', 'is_active')
actions = [mark_as_active]
6. Admin-toimintojen Testaaminen
Admin-toiminnot ovat kriittisiä yrityspohjaisen logiikan osia ja ne tulisi testata perusteellisesti. Käytä Djangon Client
-komentoa näkymien testaamiseen ja admin.ModelAdmin
-testiasiakasta spesifiin admin-toiminnallisuuteen.
# myapp/tests.py
from django.test import TestCase, Client
from django.contrib.auth import get_user_model
from django.urls import reverse
from django.contrib import admin
from .models import Product
from .admin import ProductAdmin # Tuodaan ModelAdminisi
User = get_user_model()
class ProductAdminActionTests(TestCase):
def setUp(self):
self.admin_user = User.objects.create_superuser('admin', 'admin@example.com', 'password')
self.client = Client()
self.client.login(username='admin', password='password')
self.p1 = Product.objects.create(name="Tuote A", price=10.00, is_discounted=False)
self.p2 = Product.objects.create(name="Tuote B", price=20.00, is_discounted=False)
self.p3 = Product.objects.create(name="Tuote C", price=30.00, is_discounted=True)
self.admin_site = admin.AdminSite()
self.model_admin = ProductAdmin(Product, self.admin_site)
def test_make_discounted_action(self):
# Simuloidaan tuotteiden valintaa ja toiminnon suorittamista
change_list_url = reverse('admin:myapp_product_changelist')
response = self.client.post(change_list_url, {
admin.ACTION_CHECKBOX_NAME: [self.p1.pk, self.p2.pk],
'action': 'make_discounted',
'index': 0, # Vaaditaan joidenkin Django adminin sisäisten logiikoiden vuoksi
}, follow=True)
self.assertEqual(response.status_code, 200)
self.assertContains(response, '2 tuotetta merkittiin onnistuneesti alennetuiksi.')
self.p1.refresh_from_db()
self.p2.refresh_from_db()
self.p3.refresh_from_db()
self.assertTrue(self.p1.is_discounted)
self.assertTrue(self.p2.is_discounted)
self.assertTrue(self.p3.is_discounted) # Tämä oli jo alennettu
def test_make_discounted_action_confirmation(self):
# Vahvistuksen vaativien toimintojen osalta testattaisiin kaksivaiheinen prosessi
change_list_url = reverse('admin:myapp_post_changelist') # Oletetaan Post-malli vahvistusesimerkkiä varten
post1 = Post.objects.create(title='Testijulkaisu 1', content='...', status='draft')
post2 = Post.objects.create(title='Testijulkaisu 2', content='...', status='draft')
# Vaihe 1: Vahvistussivun pyytäminen
response = self.client.post(change_list_url, {
admin.ACTION_CHECKBOX_NAME: [post1.pk, post2.pk],
'action': 'mark_posts_approved',
'index': 0,
})
self.assertEqual(response.status_code, 200)
self.assertIn(b"Vahvista toiminto", response.content) # Tarkistetaan, että vahvistussivu renderöidään
# Vaihe 2: Vahvistuslomakkeen lähettäminen
response = self.client.post(change_list_url, {
admin.ACTION_CHECKBOX_NAME: [post1.pk, post2.pk],
'action': 'mark_posts_approved',
'apply': 'Kyllä, olen varma',
'confirm': 'on', # Arvo checkboxille, jos renderöity checkboxina
'_selected_action': [str(post1.pk), str(post2.pk)], # Täytyy välittää takaisin lomakkeesta
'index': 0,
}, follow=True)
self.assertEqual(response.status_code, 200)
self.assertContains(response, '2 julkaisua merkittiin onnistuneesti hyväksytyiksi.')
post1.refresh_from_db()
post2.refresh_from_db()
self.assertEqual(post1.status, 'approved')
self.assertEqual(post2.status, 'approved')
7. Turvallisuuden Parhaat Käytännöt
- Syötteen Validointi: Validoi aina käyttäjän syötteet (vahvistuslomakkeista jne.) käyttämällä Django-lomakkeita. Älä koskaan luota raakaan käyttäjän syötteeseen.
- CSRF-suojaus: Varmista, että kaikki lomakkeet (mukaan lukien mukautetut lomakkeet mallissasi) sisältävät
{% csrf_token %}
. - SQL-injektio: Django ORM suojaa SQL-injektiolta oletusarvoisesti. Ole kuitenkin varovainen, jos joudut koskaan käyttämään raakaa SQL:ää monimutkaisissa kyselyissä toiminnoissasi.
- Arkaluonteiset Tiedot: Käsittele arkaluonteisia tietoja (API-avaimia, henkilötietoja) turvallisesti. Älä kirjaa niitä tarpeettomasti ja varmista asianmukaiset käyttöoikeudet.
Yleiset Sudenkuopat ja Ratkaisut
Jopa kokeneet kehittäjät voivat kohdata ongelmia admin-toimintojen kanssa. Tässä on joitain yleisiä sudenkuoppia:
-
return HttpResponseRedirect
-unohdus:Sudenkuoppa: Onnistuneen toiminnon jälkeen, joka ei renderöi uutta sivua (kuten vienti), unohtaa palauttaa
HttpResponseRedirect
. Sivu saattaa päivittyä, mutta onnistumisviesti ei näy, tai toiminto saattaa suorittua kahdesti selaimen päivityksen yhteydessä.Ratkaisu: Lopeta aina toimintofunktiosi
return HttpResponseRedirect(request.get_full_path())
(tai spesifi URL) -komennolla toiminnon logiikan valmistuttua, ellei kyseessä ole tiedoston (kuten CSV) tarjoaminen tai toisen sivun renderöinti. -
POST
jaGET
-käsittelyn puute vahvistuslomakkeissa:Sudenkuoppa: Toimintoon tulevan ensisijaisen pyynnön ja sen jälkeisen lomakkeen lähetyksen käsittely samana, mikä johtaa toimintojen suorittumiseen ilman vahvistusta tai lomakkeiden virheelliseen näyttämiseen.
Ratkaisu: Käytä ehdollista logiikkaa (esim.
if 'apply' in request.POST:
tairequest.method == 'POST'
) erottamaan ensisijainen pyyntö (näytä lomake) ja vahvistuksen lähetys (käsittele data). -
Suorituskykyongelmat suurilla kyselyjoukoilla:
Sudenkuoppa: Tuhansien objektien läpikäynti ja
.save()
-kutsun tekeminen jokaiselle, tai monimutkaisten laskelmien suorittaminen synkronisesti jokaiselle valitulle kohteelle.Ratkaisu: Käytä
queryset.update()
massakenttien muutoksia varten. Monimutkaisia, pitkäkestoisia tai I/O-sidonnaisia tehtäviä varten hyödynnä asynkronista käsittelyä Celeryn avulla. Harkitse sivutusta tai rajoituksia, jos toiminto on todella tarkoitettu vain pienille alijoukoille. -
Valittujen objektien ID:iden väärä välitys:
Sudenkuoppa: Vahvistussivuja toteuttaessa unohtaa välittää
_selected_action
-piilotettu syöte, joka sisältää valittujen objektien ensisijaiset avaimet, ensisijaisesta POST-pyynnöstä vahvistuslomakkeeseen ja sitten takaisin lopulliseen POST-pyyntöön.Ratkaisu: Varmista, että vahvistuslomakkeesi ja mallisi käsittelevät oikein
request.POST.getlist(admin.ACTION_CHECKBOX_NAME)
ja upottavat nämä ID:t takaisin piilotettuina syötteinä vahvistuslomakkeeseen. -
Oikeuksien ristiriidat tai puuttuvat oikeudet:
Sudenkuoppa: Toiminto ei ilmesty ylläpitäjälle tai he saavat virheen "oikeus evätty", vaikka näyttäisi siltä, että heillä pitäisi olla pääsy.
Ratkaisu: Tarkista uudelleen
get_actions()
-korvaus ja kaikkirequest.user.has_perm()
-tarkistukset toiminnossa. Varmista, että mukautetut oikeudet on määriteltyMeta
-luokassa ja migraatiot on ajettu. Tarkista käyttäjän/ryhmän määritykset adminissa.
Johtopäätös: Django Adminin Tehon Vahvistaminen
Django Admin -käyttöliittymä on paljon enemmän kuin pelkkä yksinkertainen datanhallintatyökalu; se on tehokas kehys monimutkaisten hallinnollisten työnkulkujen rakentamiseen. Hyödyntämällä mukautettuja admin-toimintoja voit laajentaa sen kyvykkyyksiä vastaamaan käytännössä mitä tahansa yritystarvetta, aina yksinkertaisista massapäivityksistä monimutkaisiin integraatioihin ulkoisiin järjestelmiin ja räätälöityjen raporttien luomiseen.
Tämä opas on käynyt läpi peruskäsitteet, käytännön toteutukset ja edistyneet tekniikat vankkojen, turvallisten ja käyttäjäystävällisten admin-toimintojen luomiseksi. Muista priorisoida käyttäjäpalautteen antaminen, toteuttaa vahva virheenkäsittely, harkita suorituskykyä suurille dataseteille ja aina ylläpitää asianmukaista valtuutusta. Näillä periaatteilla varustettuna olet nyt valmis vapauttamaan Django Adminin täyden potentiaalin, tehden siitä entistäkin korvaamattomamman resurssin sovellustesi ja datasi globaaliin hallintaan.
Aloita mukautettujen toimintojen kokeileminen tänään ja katso, kuinka hallinnollinen tehokkuutesi kasvaa!